<!DOCTYPE html>
<html lang="en">

<!-- Head tag -->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="李修倩在 Github 上的个人博客">
    <meta name="keyword" content="null">
    <meta name="theme-color" content="#600090">
    <meta name="msapplication-navbutton-color" content="#600090">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="#600090">
    <link rel="shortcut icon" href="https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-person-128.png">
    <link rel="alternate" type="application/atom+xml" title="Xiuqianli&#39;s blog" href="/atom.xml">
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.css">
    <title>
        
        手写山寨spring基础功能 - IOC｜xiuqianli&#39;s blog
        
    </title>

    <link rel="canonical" href="https://981764793.gitee.io/2018/07/17/手写山寨spring基础功能-IOC/">

    <!-- Bootstrap Core CSS -->
    <link rel="stylesheet" href="/css/bootstrap.min.css">

    <!-- Custom CSS -->
    <link rel="stylesheet" href="/css/blog-style.css">

    <!-- Pygments Github CSS -->
    <link rel="stylesheet" href="/css/syntax.css">
</head>

<style>

    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }
</style>
<!-- hack iOS CSS :active style -->
<body ontouchstart="" class="animated fadeIn">
<!-- Navigation -->
<nav class="navbar navbar-default navbar-custom navbar-fixed-top " id="nav-top" data-ispost = "true" data-istags="false
" data-ishome = "false" >
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header page-scroll">
            <button type="button" class="navbar-toggle">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand animated pulse" href="/">
                <span class="brand-logo">
                    Xiuqianli&#39;s blog
                </span>
                's Blog
            </a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <!-- Known Issue, found by Hux:
            <nav>'s height woule be hold on by its content.
            so, when navbar scale out, the <nav> will cover tags.
            also mask any touch event of tags, unfortunately.
        -->
        <!-- /.navbar-collapse -->
        <div id="huxblog_navbar">
            <div class="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="/">Home</a>
                    </li>
					
                    
                        
							
                        <li>
                            <a href="/Tags/">Tags</a>
                        </li>
							
						
                    
					
					
                </ul>
            </div>
        </div>
    </div>
    <!-- /.container -->
</nav>
<script>
    // Drop Bootstarp low-performance Navbar
    // Use customize navbar with high-quality material design animation
    // in high-perf jank-free CSS3 implementation
//    var $body   = document.body;
    var $toggle = document.querySelector('.navbar-toggle');
    var $navbar = document.querySelector('#huxblog_navbar');
    var $collapse = document.querySelector('.navbar-collapse');

    $toggle.addEventListener('click', handleMagic)
    function handleMagic(e){
        if ($navbar.className.indexOf('in') > 0) {
        // CLOSE
            $navbar.className = " ";
            // wait until animation end.
            setTimeout(function(){
                // prevent frequently toggle
                if($navbar.className.indexOf('in') < 0) {
                    $collapse.style.height = "0px"
                }
            },400)
        }else{
        // OPEN
            $collapse.style.height = "auto"
            $navbar.className += " in";
        }
    }
</script>

<!-- Main Content -->

<!--only post-->


<img class="wechat-title-img"
     src="http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80">


<style>
    
    header.intro-header {
        background-image: url('http://oco8bvfr8.qnssl.com/blog-bg-black-run.jpg?imageView2/1/w/1400/h/400/interlace/1/q/80')
    }

    
</style>

<header class="intro-header">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <div class="post-heading">
                    <h1>手写山寨spring基础功能 - IOC</h1>
                    
                    <span class="meta">
                         作者 Xiuqian Li
                        <span>
                          日期 2018-07-17
                         </span>
                    </span>
                    <div class="tags text-center">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="post-title-haojen">
        <span>
            手写山寨spring基础功能 - IOC
        </span>
    </div>
</header>

<!-- Post Content -->
<article>
    <div class="container">
        <div class="row">
            <!-- Post Container -->
            <div class="col-lg-8 col-lg-offset-1 col-sm-9 post-container">
                <h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>秋招快要到了，发现自己还是可以说是啥都没有准备，准备研究一下spring，可是发现spring那一套东西真是太完善也太庞大了，所以转移方向准备学习其他的开源项目，再看相关的博文，曲线救国的方式写了这个项目来帮助自己重新理解spring的部分原理。项目取名为LSFramework，意思当然就是Learn Spring Framework了，做完之后效果估计还是没有完全吃透spring源码来得好，但还算是有部分收获。这是一个做完之后的总结笔记，所以不适合完全跟着一步步完成。仅供参考，也请各位大佬多提意见。</p>
<h3 id="成果地址："><a href="#成果地址：" class="headerlink" title="成果地址："></a>成果地址：</h3><p><a href="https://gitee.com/981764793/LSFramework" target="_blank" rel="noopener">LSFramework - gitee</a></p>
<p><a href="https://github.com/xiuqianli1996/LSFramework" target="_blank" rel="noopener">LSFramework - github</a></p>
<p>根据自己的理解以及网上大神的各种分析，IOC都可以认为是spring最基础的东西了，所以第一步准备实现简单的IOC容器。</p>
<h2 id="IOC实现步骤"><a href="#IOC实现步骤" class="headerlink" title="IOC实现步骤"></a>IOC实现步骤</h2><h4 id="扫描class"><a href="#扫描class" class="headerlink" title="扫描class"></a>扫描class</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public static ClassLoader getClassLoader() &#123;</span><br><span class="line">        return Thread.currentThread().getContextClassLoader();</span><br><span class="line">    &#125;</span><br><span class="line">public static Set&lt;Class&lt;?&gt;&gt; scanClassListByPkg(String packageName) &#123;</span><br><span class="line">//        URL url = getClassLoader().getResource(packageName.replace(&quot;.&quot;, &quot;/&quot;));</span><br><span class="line">        if (StringKit.isBlank(packageName)) &#123;</span><br><span class="line">            throw new RuntimeException(&quot;scan package can not be empty&quot;);</span><br><span class="line">        &#125;</span><br><span class="line">        Set&lt;Class&lt;?&gt;&gt; classSet = new HashSet&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        try &#123;</span><br><span class="line">            Enumeration&lt;URL&gt; urlEnumeration = getClassLoader().getResources(packageName.replace(&quot;.&quot;, &quot;/&quot;));</span><br><span class="line">            while (urlEnumeration.hasMoreElements()) &#123;</span><br><span class="line">                URL url = urlEnumeration.nextElement();</span><br><span class="line">//                System.out.println(url);</span><br><span class="line">                String protocol = url.getProtocol();</span><br><span class="line">                if (&quot;file&quot;.equals(protocol)) &#123;</span><br><span class="line">                    addFileClass(classSet, url.getFile(), packageName);</span><br><span class="line">                &#125; else if (&quot;jar&quot;.equals(protocol)) &#123;</span><br><span class="line">                    addJarClass(classSet, (JarURLConnection) url.openConnection(), packageName);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            return classSet;</span><br><span class="line">        &#125; catch (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        return null;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    private static void addFileClass(Set&lt;Class&lt;?&gt;&gt; classSet, String packagePath, String packageName) &#123;</span><br><span class="line">        File classDir = new File(packagePath);</span><br><span class="line">//        System.out.println(classDir.exists());</span><br><span class="line">        File[] files = classDir.listFiles(new FileFilter() &#123;</span><br><span class="line">            @Override</span><br><span class="line">            public boolean accept(File file) &#123;</span><br><span class="line">                return (file.isFile() &amp;&amp; file.getName().endsWith(&quot;.class&quot;)) || file.isDirectory();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        if (CollectionKit.isEmptyArray(files))</span><br><span class="line">            return ;</span><br><span class="line">        for (File file : files) &#123;</span><br><span class="line">            String fileJavaName = StringKit.isBlank(packageName) ? file.getName() : packageName + &apos;.&apos; + file.getName();</span><br><span class="line">            if (file.isDirectory()) &#123;</span><br><span class="line"></span><br><span class="line">                Set&lt;Class&lt;?&gt;&gt; set = scanClassListByPkg(fileJavaName);//递归扫描</span><br><span class="line">                if (set != null)</span><br><span class="line">                    classSet.addAll(set);</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                String className = fileJavaName.replace(&quot;.class&quot;, &quot;&quot;);</span><br><span class="line">                addClass(classSet, className);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private static void addJarClass(Set&lt;Class&lt;?&gt;&gt; classSet, JarURLConnection urlConnection, String pkgName) &#123;</span><br><span class="line">        if (urlConnection == null)</span><br><span class="line">            return;</span><br><span class="line">        try &#123;</span><br><span class="line">            JarFile jarFile = urlConnection.getJarFile();</span><br><span class="line">            if (jarFile == null)</span><br><span class="line">                return;</span><br><span class="line">            Enumeration&lt;JarEntry&gt; jarEntryEnumeration = jarFile.entries();</span><br><span class="line">            while (jarEntryEnumeration.hasMoreElements()) &#123;</span><br><span class="line">                JarEntry jarEntry = jarEntryEnumeration.nextElement();</span><br><span class="line">                String entryName = jarEntry.getName().replaceAll(&quot;/&quot;, &quot;.&quot;);</span><br><span class="line">                if (entryName.endsWith(&quot;.class&quot;) &amp;&amp; entryName.startsWith(pkgName)) &#123;</span><br><span class="line">                    addClass(classSet, entryName.replace(&quot;.class&quot;, &quot;&quot;));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; catch (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private static void addClass(Set&lt;Class&lt;?&gt;&gt; classSet, String className) &#123;</span><br><span class="line">        Class&lt;?&gt; clazz;</span><br><span class="line">        try &#123;</span><br><span class="line">            clazz = Class.forName(className);</span><br><span class="line">            classSet.add(clazz);</span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            throw new RuntimeException(&quot;Can not load class:&quot; + className);</span><br><span class="line">        &#125; catch (NoClassDefFoundError e) &#123;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>代码比较简单，全都能语义理解，没加注释，这样就实现了递归加载指定包名下的所有类。</p>
<h5 id="再写几个从集合里过滤指定类的辅助函数"><a href="#再写几个从集合里过滤指定类的辅助函数" class="headerlink" title="再写几个从集合里过滤指定类的辅助函数"></a>再写几个从集合里过滤指定类的辅助函数</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public static Set&lt;Class&lt;?&gt;&gt; getClassesByAnnotation(Set&lt;Class&lt;?&gt;&gt; classSet, Class&lt;? extends Annotation&gt; annotationClass) &#123;</span><br><span class="line">        return classSet.stream()</span><br><span class="line">                .filter(clazz -&gt; clazz.isAnnotationPresent(annotationClass))</span><br><span class="line">                .collect(Collectors.toSet());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Set&lt;Class&lt;?&gt;&gt; getClassesBySuper(Set&lt;Class&lt;?&gt;&gt; classSet, Class&lt;?&gt; superClass) &#123;</span><br><span class="line">        return classSet.stream()</span><br><span class="line">                .filter(superClass::isAssignableFrom)</span><br><span class="line">                .collect(Collectors.toSet());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Set&lt;Class&lt;?&gt;&gt; getClassesByPkg(Set&lt;Class&lt;?&gt;&gt; classSet, String pkg) &#123;</span><br><span class="line">        return classSet.stream()</span><br><span class="line">                .filter(clazz -&gt; clazz.getName().startsWith(pkg))</span><br><span class="line">                .collect(Collectors.toSet());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Set&lt;Class&lt;?&gt;&gt; getClassesByInterface(Set&lt;Class&lt;?&gt;&gt; classSet, Class&lt;?&gt; interfaceClass) &#123;</span><br><span class="line">        return classSet.stream()</span><br><span class="line">                .filter(clazz -&gt; CollectionKit.inArray(clazz.getInterfaces(), interfaceClass))</span><br><span class="line">                .collect(Collectors.toSet());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<h4 id="实现bean容器"><a href="#实现bean容器" class="headerlink" title="实现bean容器"></a>实现bean容器</h4><p>bean容器其实就是一个HashMap，我这里是直接以String作为键类型，值直接存的bean实例，spring不直接存bean实例，而是存了一个包装对象，多包装了好几层，从而实现了根据@scope不同的bean实例加载类型（单例、原型、request等），我这里就默认全部单例了。</p>
<p>LSBean注解<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">@Target(&#123;ElementType.TYPE, ElementType.METHOD&#125;) //指定作用于类和方法</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME) //指定运行时可见</span><br><span class="line">@Documented</span><br><span class="line">public @interface LSBean &#123;</span><br><span class="line">    String value() default &quot;&quot;;//预留定义beanName</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class BeanContainer &#123;</span><br><span class="line"></span><br><span class="line">    private final static Map&lt;String, Object&gt; beanMap = new ConcurrentHashMap&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    public static boolean containsKey(String key) &#123;</span><br><span class="line">        return beanMap.containsKey(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static &lt;T&gt; T getBean(String key) &#123;</span><br><span class="line">        return (T) beanMap.get(key);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static &lt;T&gt; T getBean(Class&lt;T&gt; clazz) &#123;</span><br><span class="line">        return getBean(clazz.getName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Object put(String key, Object value) &#123;</span><br><span class="line">        return beanMap.put(key, value);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Collection&lt;Object&gt; allBeans() &#123;</span><br><span class="line">        return beanMap.values();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static Map&lt;String, Object&gt; getBeanMap() &#123;</span><br><span class="line">        return beanMap;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static void putBeanByAnnotation(Class&lt;?&gt; clazz, LSBean lsBean, Object instance) &#123;</span><br><span class="line">        put(clazz.getName(), instance); //先按类名存一次</span><br><span class="line">        String name = lsBean.value().trim();</span><br><span class="line">        if (StringKit.notBlank(name)) &#123;</span><br><span class="line">            put(name, instance); //再按注解名存一次</span><br><span class="line">        &#125;</span><br><span class="line">        Class&lt;?&gt;[] interfaces = clazz.getInterfaces();</span><br><span class="line"></span><br><span class="line">        if (!CollectionKit.isEmptyArray(interfaces)) &#123;//再按实现的接口的类名存一次</span><br><span class="line">            for (Class interfaceClass : interfaces) &#123;</span><br><span class="line">                put(interfaceClass.getName(), instance);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>偷懒直接牺牲空间省事，根据各种名字存储覆盖。接下来是定义BeanFactory接口和各种类实例化存入bean容器，必须有无参构造函数。传入的参数就是扫描出来的所有class</p>
<h4 id="根据注解加载bean实例并保存到bean容器"><a href="#根据注解加载bean实例并保存到bean容器" class="headerlink" title="根据注解加载bean实例并保存到bean容器"></a>根据注解加载bean实例并保存到bean容器</h4><h5 id="根据LSBean注解加载"><a href="#根据LSBean注解加载" class="headerlink" title="根据LSBean注解加载"></a>根据LSBean注解加载</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public interface BeanFactory &#123;</span><br><span class="line">    void loadBean(Set&lt;Class&lt;?&gt;&gt; classSet);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">public class AnnotationBeanFactory implements BeanFactory &#123;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void loadBean(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line">        if (CollectionKit.isEmptyCollection(classSet)) &#123;</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        //加载LSBean注解的类</span><br><span class="line">        ClassUtil.getClassesByAnnotation(classSet, LSBean.class)</span><br><span class="line">                .forEach(clazz -&gt; &#123;</span><br><span class="line">                    Object instance = ObjectKit.getInstance(clazz);</span><br><span class="line">                    LSBean lsBean = clazz.getAnnotation(LSBean.class);</span><br><span class="line">                    BeanContainer.putBeanByAnnotation(clazz, lsBean, instance);</span><br><span class="line">                &#125;);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h5 id="根据LSConfiguration注解加载"><a href="#根据LSConfiguration注解加载" class="headerlink" title="根据LSConfiguration注解加载"></a>根据LSConfiguration注解加载</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 作用于类上标注为配置类，作用于方法上可执行无参忽略返回值的方法</span><br><span class="line"> */</span><br><span class="line">@Target(&#123;ElementType.TYPE, ElementType.METHOD&#125;)</span><br><span class="line">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="line">@Documented</span><br><span class="line">public @interface LSConfiguration &#123;</span><br><span class="line">    int value() default 0; //预留定义配置类加载顺序</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">public class ConfigurationBeanFactory implements BeanFactory &#123;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void loadBean(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line">        //加载LSConfiguration注解的类</span><br><span class="line">        ClassUtil.getClassesByAnnotation(classSet, LSConfiguration.class)</span><br><span class="line">                .stream()</span><br><span class="line">                .sorted((o1, o2) -&gt; &#123;</span><br><span class="line">                    LSConfiguration configuration1 = o1.getAnnotation(LSConfiguration.class);</span><br><span class="line">                    LSConfiguration configuration2 = o2.getAnnotation(LSConfiguration.class);</span><br><span class="line">                    return configuration1.value() - configuration2.value();</span><br><span class="line">                &#125;)</span><br><span class="line">                .forEach(this::loadConfiguration);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void loadConfiguration(Class&lt;?&gt; clazz) &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            Object configInstance = clazz.newInstance();</span><br><span class="line">            Method[] methods = clazz.getMethods();</span><br><span class="line">            Arrays.sort(methods, (o1, o2) -&gt; o1.getName().compareToIgnoreCase(o2.getName())); //根据名字排序控制加载顺序</span><br><span class="line">            for (Method method : methods) &#123;</span><br><span class="line">                if (method.isAnnotationPresent(LSConfiguration.class)) &#123;</span><br><span class="line">                    method.invoke(configInstance); //LSConfiguration注解的方法直接执行</span><br><span class="line">                    continue;</span><br><span class="line">                &#125;</span><br><span class="line">                if (!method.isAnnotationPresent(LSBean.class))</span><br><span class="line">                    continue;</span><br><span class="line"></span><br><span class="line">                Object[] args = getInjectParams(method);</span><br><span class="line">                Object beanInstance = method.invoke(configInstance, args);</span><br><span class="line">                Class&lt;?&gt; beanClass = method.getReturnType();</span><br><span class="line">                LSBean lsBean = method.getAnnotation(LSBean.class);</span><br><span class="line">                BeanContainer.putBeanByAnnotation(beanClass, lsBean, beanInstance);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; catch (IllegalAccessException | InstantiationException | InvocationTargetException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    private Object[] getInjectParams(Method method) &#123;</span><br><span class="line">        Parameter[] parameters = method.getParameters();</span><br><span class="line">        Object[] args = new Object[parameters.length];</span><br><span class="line">        for (int i = 0; i &lt; parameters.length; i++) &#123;</span><br><span class="line">            Parameter parameter = parameters[i];</span><br><span class="line">            Class&lt;?&gt; typeClass = parameter.getType();</span><br><span class="line">            String beanName = typeClass.getName();</span><br><span class="line">            if (parameter.isAnnotationPresent(LSAutowired.class)) &#123;</span><br><span class="line">                LSAutowired lsAutowired = parameter.getAnnotation(LSAutowired.class);</span><br><span class="line">                if (StringKit.notBlank(lsAutowired.value())) &#123;</span><br><span class="line">                    beanName = lsAutowired.value();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            Object arg = BeanContainer.getBean(beanName);</span><br><span class="line">            if (arg == null) &#123;</span><br><span class="line">                throw new LSException(String.format(&quot;In %s inject param is null: %s&quot;</span><br><span class="line">                        , ClassUtil.getFullMethodName(method), beanName));</span><br><span class="line">            &#125;</span><br><span class="line">            args[i] = AopHelper.enhance(arg); //直接尝试进行Aop强化</span><br><span class="line">        &#125;</span><br><span class="line">        return args;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h5 id="根据json配置文件加载-2018-07-12更新，修改代码解决属性注入的循环依赖问题"><a href="#根据json配置文件加载-2018-07-12更新，修改代码解决属性注入的循环依赖问题" class="headerlink" title="根据json配置文件加载 2018.07.12更新，修改代码解决属性注入的循环依赖问题"></a>根据json配置文件加载 2018.07.12更新，修改代码解决属性注入的循环依赖问题</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">public class JsonBeanFactory implements BeanFactory &#123;</span><br><span class="line"></span><br><span class="line">    private List&lt;BeanInfo&gt; beanInfoList = null;</span><br><span class="line">    private final static Pattern BEAN_REF_PATTERN = Pattern.compile(&quot;\\$\\&#123;(\\w+)\\&#125;&quot;); //对象关系占位符正则匹配</span><br><span class="line"></span><br><span class="line">    private void initBeanInfoList(String configPath) &#123;</span><br><span class="line">        InputStream inputStream = ClassUtil.getClassLoader().getResourceAsStream(configPath);</span><br><span class="line">        if (inputStream == null) &#123;</span><br><span class="line">            return;</span><br><span class="line">        &#125;</span><br><span class="line">        byte[] buffer = new byte[0];</span><br><span class="line">        try &#123;</span><br><span class="line">            buffer = new byte[inputStream.available()];</span><br><span class="line">            inputStream.read(buffer);</span><br><span class="line">            inputStream.close();</span><br><span class="line">        &#125; catch (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        if (buffer.length &gt; 0) &#123;</span><br><span class="line">            beanInfoList = new Gson().fromJson(new String(buffer), new TypeToken&lt;List&lt;BeanInfo&gt;&gt;()&#123;&#125;.getType()); // gson解析BeanInfo集合</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void loadBean(Set&lt;Class&lt;?&gt;&gt; classSet) &#123;</span><br><span class="line">        String configPath = PropKit.get(Constants.CONFIG_BEANS_CONFIG);</span><br><span class="line">        if (StringKit.isBlank(configPath)) &#123;</span><br><span class="line">            throw new LSException(&quot;beansConfig path is null, can not load bean&quot;);</span><br><span class="line">        &#125;</span><br><span class="line">        initBeanInfoList(configPath);</span><br><span class="line">        if (CollectionKit.isEmptyCollection(beanInfoList))</span><br><span class="line">            return;</span><br><span class="line">        beanInfoList.forEach(this::addBeanByBeanInfo);</span><br><span class="line">        //修改成先把bean实例存到容器再对属性进行注入</span><br><span class="line">        beanInfoList.forEach(beanInfo -&gt; &#123;</span><br><span class="line">            Object instance = BeanContainer.getBean(beanInfo.getName());</span><br><span class="line">            try &#123;</span><br><span class="line">                injectProperty(instance, beanInfo.getProperties(), instance.getClass());</span><br><span class="line">            &#125; catch (IllegalAccessException | NoSuchFieldException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">                throw new LSException(e.getCause());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void addBeanByBeanInfo(BeanInfo beanInfo) &#123;</span><br><span class="line">        String className = beanInfo.getClassName();</span><br><span class="line">        String beanName = beanInfo.getName();</span><br><span class="line"></span><br><span class="line">        if (StringKit.isBlank(beanName) || StringKit.isBlank(className)) &#123;</span><br><span class="line">            throw new LSException(&quot;beanName or className can not be null&quot;);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        try &#123;</span><br><span class="line">            Class&lt;?&gt; clazz = Class.forName(className);</span><br><span class="line"></span><br><span class="line">            Object instance = getInstanceByConstructor(beanInfo.getConstructor(), clazz);</span><br><span class="line">            if (instance == null) &#123;</span><br><span class="line">                instance = clazz.newInstance(); //尝试根据构造函数新建实例失败直接调用无参构造函数</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            BeanContainer.put(beanName, instance); // 按定义的名字存一遍</span><br><span class="line">            BeanContainer.put(className, instance);// 再按类名存一遍</span><br><span class="line"></span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 根据properties的内容对instance进行成员变量注入</span><br><span class="line">     * @param instance</span><br><span class="line">     * @param propertiesMap</span><br><span class="line">     * @param clazz</span><br><span class="line">     * @return</span><br><span class="line">     * @throws IllegalAccessException</span><br><span class="line">     * @throws NoSuchFieldException</span><br><span class="line">     */</span><br><span class="line">    private Object injectProperty(Object instance, Map&lt;String, String&gt; propertiesMap, Class&lt;?&gt; clazz) throws IllegalAccessException</span><br><span class="line">            , NoSuchFieldException &#123;</span><br><span class="line"></span><br><span class="line">        //进行成员变量注入</span><br><span class="line">        if (propertiesMap != null &amp;&amp; propertiesMap.size() &gt; 0) &#123;</span><br><span class="line">            for (Map.Entry&lt;String, String&gt; entry : propertiesMap.entrySet()) &#123;</span><br><span class="line">                String name = entry.getKey();</span><br><span class="line">                String value = entry.getValue();</span><br><span class="line">                Field field = clazz.getDeclaredField(name);</span><br><span class="line">                field.setAccessible(true);</span><br><span class="line">                if (Modifier.isFinal(field.getModifiers())) &#123;</span><br><span class="line">                    throw new DiException(clazz.getName() + &quot;:&quot; + field.getName());</span><br><span class="line">                &#125;</span><br><span class="line">                field.set(instance, getPropertyObj(value, field.getType()));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        return instance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 尝试根据constructor内容进行构造函数注入并返回实例，constructor内参数长度、顺序必须和某一构造函数一致</span><br><span class="line">     * @param constructorParamList</span><br><span class="line">     * @param clazz</span><br><span class="line">     * @return</span><br><span class="line">     * @throws IllegalAccessException</span><br><span class="line">     * @throws InvocationTargetException</span><br><span class="line">     * @throws InstantiationException</span><br><span class="line">     */</span><br><span class="line">    private Object getInstanceByConstructor(List&lt;String&gt; constructorParamList, Class&lt;?&gt; clazz) throws IllegalAccessException, InvocationTargetException, InstantiationException &#123;</span><br><span class="line">        int constructorParamCount = constructorParamList == null ? 0 : constructorParamList.size();</span><br><span class="line">        Object instance = null;</span><br><span class="line">        //尝试进行构造函数注入，构造函数参数个数必须与配置文件中的参数个数相同</span><br><span class="line">        Constructor[] constructors = clazz.getDeclaredConstructors();</span><br><span class="line">        if (constructorParamCount &gt; 0 &amp;&amp; constructors.length &gt; 0) &#123;</span><br><span class="line">            Constructor targetConstructor = null;</span><br><span class="line">            for (Constructor constructor : constructors) &#123;</span><br><span class="line">                if (constructor.getParameterCount() == constructorParamCount) &#123;</span><br><span class="line">                    targetConstructor = constructor;</span><br><span class="line">                    break;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            if (targetConstructor != null) &#123;</span><br><span class="line">                Parameter[] params = targetConstructor.getParameters();</span><br><span class="line">                Object[] args = new Object[constructorParamCount];</span><br><span class="line">                int pos = 0;</span><br><span class="line">                for (Parameter parameter : params) &#123; //填充构造函数参数</span><br><span class="line">                    Class type = parameter.getType();</span><br><span class="line">                    String value = constructorParamList.get(pos);</span><br><span class="line">                    args[pos++] = getPropertyObj(value, type);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                instance = targetConstructor.newInstance(args);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return instance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 获取属性的对象，根据参数占位符判断是否引用其他对象对象，基本类型直接调用工具类转换成相应的类型</span><br><span class="line">     * @param value</span><br><span class="line">     * @param type</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    private Object getPropertyObj(String value, Class type) &#123;</span><br><span class="line">        if (StringKit.isBlank(value))</span><br><span class="line">            return null;</span><br><span class="line">        Matcher matcher = BEAN_REF_PATTERN.matcher(value);</span><br><span class="line">        if (matcher.find()) &#123;</span><br><span class="line">            String argName = matcher.group(1);</span><br><span class="line">            Object arg = BeanContainer.getBean(argName);</span><br><span class="line">            if (arg == null) &#123;</span><br><span class="line">                throw new RuntimeException(&quot;can not found bean by name is &quot; + argName);</span><br><span class="line">            &#125;</span><br><span class="line">            if (arg.getClass() != type) &#123;</span><br><span class="line">                throw new RuntimeException(&quot;can not found bean by name is &quot; + argName + &quot;,type is wrong&quot;);</span><br><span class="line">            &#125;</span><br><span class="line">            return AopHelper.enhance(arg);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">            return ConvertUtil.convert(value, type);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>至此ioc的bean初始化就完事了，还附带着json文件的依赖注入也完事了，还有LSConfiguration注解的配置类里@LSBean注解的方法的依赖注入也完事了，接下来就开始实现@LSAutowired对bean容器里的对象实现依赖注入</p>
<h4 id="LSAutowired实现成员变量和setter依赖注入"><a href="#LSAutowired实现成员变量和setter依赖注入" class="headerlink" title="@LSAutowired实现成员变量和setter依赖注入"></a>@LSAutowired实现成员变量和setter依赖注入</h4><p>写个DependencyInjector工具类</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">public class DependencyInjector &#123;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 遍历bean容器里的所有对象，依次注入</span><br><span class="line">     */</span><br><span class="line">    public static void injectAll() &#123;</span><br><span class="line">        DependencyInjector injector = new DependencyInjector();</span><br><span class="line">        for (Object obj : BeanContainer.allBeans()) &#123;</span><br><span class="line">            injector.inject(obj);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    public void inject(Object obj) &#123;</span><br><span class="line">        Class&lt;?&gt; clazz = obj.getClass();</span><br><span class="line">        injectByField(clazz, obj);</span><br><span class="line">        injectBySetMethod(clazz, obj);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 遍历成员变量进行注入</span><br><span class="line">     * @param clazz</span><br><span class="line">     * @param obj</span><br><span class="line">     */</span><br><span class="line">    private void injectByField(Class&lt;?&gt; clazz, Object obj) &#123;</span><br><span class="line">        Field[] fields = clazz.getDeclaredFields();</span><br><span class="line">        for (Field field : fields) &#123;</span><br><span class="line">            if (!field.isAnnotationPresent(LSAutowired.class)) &#123;</span><br><span class="line">                continue;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            if (Modifier.isFinal(field.getModifiers())) &#123;</span><br><span class="line">                throw new DiException(clazz.getName() + &quot;:&quot; + field.getName());</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            try &#123;</span><br><span class="line">                field.setAccessible(true);</span><br><span class="line">//                if (field.get(obj) != null) &#123;</span><br><span class="line">//                    continue;//已经有值不覆盖注入</span><br><span class="line">//                &#125;</span><br><span class="line"></span><br><span class="line">                LSAutowired lsAutowired = field.getAnnotation(LSAutowired.class);</span><br><span class="line">                Object val = getInjectVal(lsAutowired, field.getType().getName());</span><br><span class="line">                field.set(obj, val); //注入Aop强化后的对象</span><br><span class="line">            &#125; catch (IllegalAccessException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">                throw new DiException(e.getMessage());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 必须是public set开头 只有一个参数 LSAutowired注解的方法才会调用注入</span><br><span class="line">     * @param clazz</span><br><span class="line">     * @param obj</span><br><span class="line">     */</span><br><span class="line">    private void injectBySetMethod(Class&lt;?&gt; clazz, Object obj) &#123;</span><br><span class="line">        Method[] methods = clazz.getMethods();</span><br><span class="line">        for (Method method : methods) &#123;</span><br><span class="line">            if (!method.getName().startsWith(&quot;set&quot;) || method.getParameterCount() != 1) &#123;</span><br><span class="line">                continue;</span><br><span class="line">            &#125;</span><br><span class="line">            if (!method.isAnnotationPresent(LSAutowired.class)) &#123;</span><br><span class="line">                continue;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            LSAutowired lsAutowired = method.getAnnotation(LSAutowired.class);</span><br><span class="line">            Class&lt;?&gt; paramClass = method.getParameterTypes()[0];</span><br><span class="line">            Object val = getInjectVal(lsAutowired, paramClass.getName());</span><br><span class="line"></span><br><span class="line">            try &#123;</span><br><span class="line">                method.invoke(obj, val);</span><br><span class="line">            &#125; catch (IllegalAccessException | InvocationTargetException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">                throw new DiException(e.getMessage());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 尝试根据LSAutowired给的beanName从容器里获取对象，为空就按类名获取</span><br><span class="line">     * @param lsAutowired</span><br><span class="line">     * @param className</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    private Object getInjectVal(LSAutowired lsAutowired, String className) &#123;</span><br><span class="line">        String beanName = lsAutowired.value();</span><br><span class="line">        if (StringKit.isBlank(beanName)) &#123;</span><br><span class="line">            beanName = className;</span><br><span class="line">        &#125;</span><br><span class="line">        Object val = BeanContainer.getBean(beanName);</span><br><span class="line">        if (val == null) &#123;</span><br><span class="line">//            throw new DiException(beanName + &quot;is not found in bean container&quot;);</span><br><span class="line">            return null;</span><br><span class="line">        &#125;</span><br><span class="line">        return AopHelper.enhance(val);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>执行顺序优先级是成员变量 &gt; setter，生效优先级是setter &gt; 成员变量。<br>再完成各个BeanFactory的加载后即可执行DependencyInjector.injectAll()对容器内所有bean对象进行依赖注入，并且注入的对象是尝试进行Aop强化后的对象。</p>

                <hr>
                

                <ul class="pager">
                    
                    <li class="previous">
                        <a href="/2018/07/17/手写山寨spring基础功能-AOP/" data-toggle="tooltip" data-placement="top"
                           title="手写山寨spring基础功能 - AOP">&larr; Previous Post</a>
                    </li>
                    
                    
                    <li class="next">
                        <a href="/2018/06/02/慕课课程-java高并发秒杀（2）/" data-toggle="tooltip" data-placement="top"
                           title="慕课课程-java高并发秒杀（2）">Next Post &rarr;</a>
                    </li>
                    
                </ul>

                

                


                <!--加入新的评论系统-->
                
            </div>

            <div class="hidden-xs col-sm-3 toc-col">
                <div class="toc-wrap">
                    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#前言"><span class="toc-text">前言</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#成果地址："><span class="toc-text">成果地址：</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#IOC实现步骤"><span class="toc-text">IOC实现步骤</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#扫描class"><span class="toc-text">扫描class</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#再写几个从集合里过滤指定类的辅助函数"><span class="toc-text">再写几个从集合里过滤指定类的辅助函数</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#实现bean容器"><span class="toc-text">实现bean容器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#根据注解加载bean实例并保存到bean容器"><span class="toc-text">根据注解加载bean实例并保存到bean容器</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#根据LSBean注解加载"><span class="toc-text">根据LSBean注解加载</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#根据LSConfiguration注解加载"><span class="toc-text">根据LSConfiguration注解加载</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#根据json配置文件加载-2018-07-12更新，修改代码解决属性注入的循环依赖问题"><span class="toc-text">根据json配置文件加载 2018.07.12更新，修改代码解决属性注入的循环依赖问题</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#LSAutowired实现成员变量和setter依赖注入"><span class="toc-text">@LSAutowired实现成员变量和setter依赖注入</span></a></li></ol></li></ol></li></ol>
                </div>
            </div>
        </div>

        <div class="row">
            <!-- Sidebar Container -->

            <div class="
                col-lg-8 col-lg-offset-2
                col-md-10 col-md-offset-1
                sidebar-container">

                <!-- Featured Tags -->
                
                <section>
                    <!-- no hr -->
                    <h5 class="text-center">
                        <a href="/tags/">FEATURED TAGS</a>
                    </h5>
                    <div class="tags">
                        
                        <a class="tag" href="/tags/#java"
                           title="java">java</a>
                        
                        <a class="tag" href="/tags/#spring"
                           title="spring">spring</a>
                        
                    </div>
                </section>
                

                <!-- Friends Blog -->
                
            </div>
        </div>

    </div>
</article>







<!-- Footer -->
<!-- Footer -->
<footer>
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1 text-center">
                <br>
                <ul class="list-inline text-center">
                
                
                
                    <li>
                        <a target="_blank" href="https://www.zhihu.com/people/asdf1234-97">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa  fa-stack-1x fa-inverse">知</i>
                            </span>
                        </a>
                    </li>
                

                

                

                
                    <li>
                        <a target="_blank"  href="https://github.com/xiuqianli1996">
                            <span class="fa-stack fa-lg">
                                <i class="fa fa-circle fa-stack-2x"></i>
                                <i class="fa fa-github fa-stack-1x fa-inverse"></i>
                            </span>
                        </a>
                    </li>
                

                

                </ul>
                <p class="copyright text-muted">
                    Copyright &copy; Xiuqianli&#39;s blog 2018
                    <br>
                    <span id="busuanzi_container_site_pv" style="font-size: 12px;">PV: <span id="busuanzi_value_site_pv"></span> Times</span>
                    <br>
                    Theme by <a href="https://haojen.github.io/">Haojen Ma</a>
                </p>

            </div>
        </div>
    </div>
</footer>

<!-- jQuery -->
<script src="/js/jquery.min.js"></script>

<!-- Bootstrap Core JavaScript -->
<script src="/js/bootstrap.min.js"></script>

<!-- Custom Theme JavaScript -->
<script src="/js/blog.js"></script>

<!-- async load function -->
<script>
    function async(u, c) {
      var d = document, t = 'script',
          o = d.createElement(t),
          s = d.getElementsByTagName(t)[0];
      o.src = u;
      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
      s.parentNode.insertBefore(o, s);
    }
</script>

<!-- jquery.tagcloud.js -->
<script>
    // only load tagcloud.js in tag.html
    if($('#tag_cloud').length !== 0){
        async("https://981764793.gitee.io/js/jquery.tagcloud.js",function(){
            $.fn.tagcloud.defaults = {
                //size: {start: 1, end: 1, unit: 'em'},
                color: {start: '#bbbbee', end: '#0085a1'},
            };
            $('#tag_cloud a').tagcloud();
        })
    }
</script>

<!--fastClick.js -->
<script>
    async("//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js", function(){
        var $nav = document.querySelector("nav");
        if($nav) FastClick.attach($nav);
    })
</script>

<!-- Google Analytics -->



<!-- Baidu Tongji -->


<!-- swiftype -->
<script type="text/javascript">
  (function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
  (w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
  e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
  })(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

  _st('install','null','2.0.0');
</script>

<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<!--wechat title img-->
<img class="wechat-title-img" src="null">
</body>

</html>
